/**
* Oxide
*
* Copyright (c) 2011 Dinosaur Kick Pty Ltd, All rights reserved.
*
* This source code is made available under the "Modified BSD" licensing arrangement.
* See license.txt for details.
*/
// ----------------------------------------------------------------------------

expr_ptr parse_expr(void)
{
	expr_ptr expr = parse_expr_unary_oper();
	if (!expr)
	{
		return expr;
	}

	expr = parse_expr_binary_oper(expr);
	
	if (!expr)
	{
		return expr;
	}

	return expr;
}

// ----------------------------------------------------------------------------

expr_ptr parse_expr_atom(void)
{
	switch (m_cursor->identity)
	{
		case TOK_OPEN_PAREN:
			return parse_expr_parens();
			
		case TOK_IDENTIFIER:
			return parse_identifier();

		case TOK_STRING:
			return parse_string();

		case TOK_INTEGER_HEX:
		case TOK_INTEGER_BIN:
		case TOK_INTEGER_OCT:
		case TOK_INTEGER_DEC:
			return parse_integer();
		
		case TOK_FLOAT:
			return parse_float();
		
		case TOK_FIXED:
			return parse_fixed();
		
		default:
			unexpected_token_error();
			m_cursor.advance();
			return expr_ptr();
	}
}

// ----------------------------------------------------------------------------

expr_ptr parse_expr_parens(void)
{
	assert(m_cursor->identity == TOK_OPEN_PAREN);
	m_cursor.advance();

	expr_ptr expr = parse_expr();

	if (!expr)
	{
		return expr;
	}
	
	if (m_cursor->identity != TOK_CLOSE_PAREN)
	{
		unexpected_token_error(TOK_CLOSE_PAREN);
		m_cursor.advance();
		return expr_ptr();
	}

	m_cursor.advance();

	return expr;
}

// ----------------------------------------------------------------------------

expr_ptr parse_expr_unary_oper(void)
{
	switch (m_cursor->identity)
	{
		case TOK_POS:
		case TOK_NEG:
		case TOK_INC:
		case TOK_DEC:
			return parse_expr_prefix_unary_oper();
		default:
			break;
	}

	expr_ptr expr = parse_expr_atom();

	switch (m_cursor->identity)
	{
		case TOK_OPEN_PAREN:
			return parse_call_expr(expr);
		case TOK_INC:
		case TOK_DEC:
			return parse_expr_postfix_unary_oper(expr);
		default:
			return expr;
	}
}

// ----------------------------------------------------------------------------

expr_ptr parse_expr_prefix_unary_oper()
{
	token_identity identity = m_cursor->identity;
	m_cursor.advance();
	return boost::make_shared<unary_oper_type>(identity, parse_expr(), false);
}

// ----------------------------------------------------------------------------

expr_ptr parse_expr_postfix_unary_oper(expr_ptr expr)
{
	token_identity identity = m_cursor->identity;
	m_cursor.advance();
	return boost::make_shared<unary_oper_type>(identity, expr, true);
}

// ----------------------------------------------------------------------------

expr_ptr parse_expr_binary_oper(expr_ptr lhs, int min_precedence = 0)
{
	int precedence;
	int next_precedence;
	expr_ptr rhs;

	while (m_cursor)
	{
		precedence = m_precedence_map[m_cursor->identity];

		if (precedence < min_precedence)
		{
			return lhs;
		}

		token_identity oper = m_cursor->identity;
		m_cursor.advance();
		
		rhs = parse_expr_unary_oper();
		if (!rhs)
		{
			return rhs;
		}

		next_precedence = m_precedence_map[m_cursor->identity];

		if (precedence < next_precedence)
		{
			rhs = parse_expr_binary_oper(rhs, precedence + 1);
			if (!rhs)
			{
				return rhs;
			}
		}
		
		lhs = boost::make_shared<binary_oper_type>(oper, lhs, rhs);
	}
	
	unexpected_token_error();
	return expr_ptr();
}

// ----------------------------------------------------------------------------

call_expr_ptr parse_call_expr(expr_ptr callable)
{
	assert(m_cursor->identity == TOK_OPEN_PAREN);
	m_cursor.advance();
	
	call_expr_ptr call = boost::make_shared<call_expr_type>(callable);
	expr_ptr argument;
	
	while (m_cursor)
	{
		if (m_cursor->identity == TOK_CLOSE_PAREN)
		{
			m_cursor.advance();
			return call;
		}
		
		argument = parse_expr();

		if (!argument)
		{
			return call;
		}
		
		call->arguments.push_back(argument);
		
		if (m_cursor->identity == TOK_COMMA)
		{
			m_cursor.advance();
		}
	}

	unexpected_token_error();
	return call;
}

// ----------------------------------------------------------------------------

identifier_ptr parse_identifier(void)
{
	string_type value = m_cursor->content;
	m_cursor.advance();
	return  boost::make_shared<identifier_type>(value);
}

// ----------------------------------------------------------------------------
